-- 2022-8-12

---[[ 错误

    -- 人人皆难免犯错误 因此 我们必须尽可能处理错误
    -- 由于Lua语言是一种经常被嵌入在应用程序中的扩展语言所以当错误发生时并不能简答的崩溃或退出
    -- 相反 只有错误发生 Lua语言就必须提供处理错误的方式

    -- Lua语言会在遇到非预期的情况时引发错误
    -- 例如 当始于将两个非数值类型的值相加 对不是函数的值进行调用 对不是表类型的值进行索引等(我们会在后续学习中使用元表(metatable)来改变上述行为)
    -- 我们也可以显式的通过调用函数error并传入一个错误的信息作为参数来引发一个错误 通常 这个函数就是在代码中提示出错的合理方式
    print "enter a number:"
    local n = io.read("n")
    if not n then error("invalid input") end
    -- 由于"针对某些情况调用函数error" 这样的代码结构太常见了 所以Lua语言提供了一个内建的函数assert来完成这类工作
    print "enter a number:"
    n = assert(io.read("*n"),"invalid input")

    -- 函数assert检查其第一个参数是否为真 如果该函数为真则返回该参数 如果该参数为真则引发一个错误
    -- 该函数的第2个参数是一个可选的错误信息 不过要注意函数assert只是一个普通函数 所以Lua语言会总是在调用该函数前先对参数进行求值
    -- 如果编写形如
    local n = io.read()
    assert(tonumber(n),"invalid input: " .. n .. " is not a number")
    -- 的代码 那么即使n是一个数值类型 Lua语言也总是会进行字符串审核 在这种情况下使用显式的测试可能更加明智
    -- 当一个函数math.sin为例 当调用时参数传入了一个表该如何反应呢？如果要检查错误 那么就不得不编写如下代码
    local res = math.sin(x)
    if not res then -- 错误？
        -- error-handling code
    end
    -- 当然也可以在调用函数前轻松的检查出这种异常
    if not tonumber(x) then -- x是否为数字?
        -- error-handling code
    end
    -- 通常 我们既不会检查参数也不会检查函数sin的返回值 如果sin的参数不是一个数值 那么就意味着我们的程序可能出现了问题
    -- 此时 处理异常最简单也是最实用的做法就是停止运行 然后输出一条错误信息

    -- 另一方面 让我们再考虑一下用于打开文件的函数io.open
    -- 如果要打开的文件不存在 那么该函数应该有怎么样的行为呢?在这种情况下 没有什么简单的方法可以在调用函数前检测到这种异常
    -- 在很多系统中 判断一个文件是否存在的唯一方法就是试着去打开这个文件
    -- 因此 如果由于外部原因(比如"文件不存在(file does not exist")或"权限不足(permission denied)")导致函数io.open无法打开一个文件 那么它应返回false及一条错误信息
    -- 通过这种方式 我们就有机会采取恰当的方式来处理异常情况 例如要求用户提供另一个文件名:
    local file,msg
    repeat
        print "enter a file name"
        local name = io.read()
        if not name then -- 没有输入
            return
        end
        file,msg = io.open(name,"r")
        if not file then
            print(msg)
        end
    until file
    -- 如果不想处理这些情况 但又想安全的运行程序 那么只需要使用assert
    local file = assert(io.open(name,"r"))
       --> stdin:1 no-file: No such file or directory
    -- 这是Lua语言中一种典型的技巧 如果函数io.open执行失败 assert就引发一个错误
    -- 请读者注意 错误信息(函数io.open的第2个返回值)是如何变成assert的第二个参数的
--]]